﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.Events;

namespace Script.Net
{
    public class TcpServer
    {
        /// <summary>
        ///     连接到当前Server的所有客户端的字典，key为ClientSocketId
        /// </summary>
        private readonly Dictionary<int, TcpClientSocketData> _clientDic;

        /// <summary>
        ///     连接到当前Server的所有客户端
        /// </summary>
        private readonly List<TcpClientSocketData> _clients;

        /// <summary>
        ///     监听链接用的Socket
        /// </summary>
        private readonly Socket _listenSocket;

        /// <summary>
        ///     当接收到数据时的回调
        /// </summary>
        private readonly UnityAction<byte[], TcpClientSocketData> _onDataArriveCallback;

        /// <summary>
        ///     等待发送的消息队列
        /// </summary>
        public List<TcpServerSendData> _waitingSendData;

        #region 接收数据

        private void EndReceiveData(IAsyncResult ar)
        {
            var tso = ar.AsyncState as TcpServerReceiveData;
            var readLen = tso.WorkClient.ClientSocket.EndReceive(ar);
            if (readLen > 0)
            {
                var transData = tso.TransData;
                if (transData.Read(readLen))
                {
                    _onDataArriveCallback.Invoke(transData.Data, tso.WorkClient);
                    transData.Reset();
                }

                tso.WorkClient.ClientSocket.BeginReceive(tso.TransData.Buffer, 0, Const.TcpBufferSize, 0,
                    EndReceiveData, tso);
            }
            else
            {
                Debug.LogError("服务端Socket断开");
            }
        }

        #endregion

        #region 生命周期及对外主要函数

        /// <summary>
        ///     创建一个TcpServer，但是不启动监听
        /// </summary>
        /// <param name="callback">当数据到来时的回调</param>
        public TcpServer(UnityAction<byte[], TcpClientSocketData> callback)
        {
            _listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _listenSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
            _listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
            _clients = new List<TcpClientSocketData>();
            _clientDic = new Dictionary<int, TcpClientSocketData>();
            _waitingSendData = new List<TcpServerSendData>();
            _onDataArriveCallback = callback;
            StartListen();
        }

        /// <summary>
        ///     主要的收发协程
        /// </summary>
        public IEnumerator MainCoroutine()
        {
            SendDataCortuine().MoveNext();
            yield return null;
        }

        /// <summary>
        ///     发送给特定客户端数据
        /// </summary>
        /// <param name="data"></param>
        /// <param name="client"></param>
        public void SendTo(byte[] data, TcpClientSocketData client)
        {
            _waitingSendData.Add(new TcpServerSendData(data, client));
        }

        /// <summary>
        ///     发送/推送数据给所有客户端
        /// </summary>
        /// <param name="data"></param>
        public void PushDataToAll(byte[] data)
        {
            for (var i = 0; i < _clients.Count; i++) _waitingSendData.Add(new TcpServerSendData(data, _clients[i]));
        }

        /// <summary>
        ///     关闭当前Server
        /// </summary>
        public void Close()
        {
            for (var i = 0; i < _clients.Count; i++)
            {
                _clients[i].ClientSocket.Shutdown(SocketShutdown.Both);
                _clients[i].ClientSocket.Close();
            }

            _listenSocket.Shutdown(SocketShutdown.Both);
            _listenSocket.Close();
        }

        #endregion

        #region 链接相关

        /// <summary>
        ///     开启监听
        /// </summary>
        private void StartListen()
        {
            _listenSocket.Bind(new IPEndPoint(IPAddress.Any, Const.TcpServerPort));
            _listenSocket.Listen(10);
            Debug.Log("本地监听已建立" + (_listenSocket.LocalEndPoint as IPEndPoint));
            CoroutineManager.Instance.StartCoroutine(ClientConnectHandlers());
        }

        /// <summary>
        ///     客户端链接处理器
        /// </summary>
        /// <returns></returns>
        private IEnumerator ClientConnectHandlers()
        {
            while (true)
            {
                if (_clients.Count >= 3) yield break;

                if (_listenSocket.Poll(1, SelectMode.SelectRead))
                {
                    var tco = new TcpClientSocketData(_listenSocket.Accept());
                    _clientDic.Add(tco.SocketId, tco);
                    _clients.Add(tco);
                    var tso = new TcpServerReceiveData(tco);
                    tco.ClientSocket.BeginReceive(tso.TransData.Buffer, 0, Const.TcpBufferSize, 0, EndReceiveData, tso);
                }

                yield return null;
            }
        }

        #endregion

        #region 发送数据

        /// <summary>
        ///     发送数据协程
        /// </summary>
        private IEnumerator SendDataCortuine()
        {
            while (true)
            {
                var i = 0;
                while (i < _waitingSendData.Count)
                {
                    var pendingData = _waitingSendData[i];
                    if (pendingData.TransfData.IsComplete)
                    {
                        _waitingSendData.RemoveAt(i);
                        continue;
                    }

                    if (pendingData.Client.IsSending)
                    {
                        i++;
                        continue;
                    }

                    pendingData.Client.IsSending = true;
                    pendingData.Client.ClientSocket.BeginSend(pendingData.TransfData.Data, 0,
                        pendingData.TransfData.TotalProgress,
                        SocketFlags.None, EndSendData, pendingData);
                    i++;
                }

                yield return new WaitForSecondsRealtime(.1f);
            }
        }

        /// <summary>
        ///     结束发送数据回调
        /// </summary>
        /// <param name="ar"></param>
        private void EndSendData(IAsyncResult ar)
        {
            var tsd = ar.AsyncState as TcpServerSendData;
            var sendLen = tsd.Client.ClientSocket.EndSend(ar);
            if (sendLen + tsd.TransfData.CurrentProgress < tsd.TransfData.TotalProgress)
                if (sendLen == 0)
                {
                    tsd.Client.IsSending = false;
                    Debug.LogError(
                        "Send Data Error TargetLength:" + tsd.TransfData.TotalProgress + " SendLength :" +
                        tsd.TransfData.CurrentProgress);
                }
                else
                {
                    tsd.TransfData.CurrentProgress += sendLen;
                    tsd.Client.ClientSocket.BeginSend(tsd.TransfData.Data, tsd.TransfData.CurrentProgress,
                        tsd.TransfData.TotalProgress - tsd.TransfData.CurrentProgress, SocketFlags.None, EndSendData,
                        tsd);
                }
            else
                tsd.Client.IsSending = false;
        }

        #endregion
    }
}